import { fileURLToPath, URL } from 'node:url'
import { mkdir, writeFile } from 'node:fs/promises'
import { join, resolve } from 'node:path'
import { defineConfig, mergeConfig, UserConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import printUrls from 'vite-plugin-print-urls'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

const baseConfig: UserConfig = {
  plugins: [
    vue(),
    vueJsx(),
    printUrls(),
    AutoImport({ resolvers: [ElementPlusResolver()] }),
    Components({ resolvers: [ElementPlusResolver()] })
  ],
  resolve: {
    alias: {
      '@shared': resolve(__dirname, 'src/shared')
    }
  },
  define: {
    __VUE_OPTIONS_API__: 'false',
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
  }
}

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
  switch (mode) {
    case 'content-dev':
    case 'color-dev':
    case 'options':
      return createVueConfig(mode)
    case 'background':
    case 'content':
    case 'inject':
      return createScriptConfig(mode)
    default:
      return {
        plugins: [vue(), vueJsx()],
        resolve: {
          alias: {
            '@': fileURLToPath(new URL('./src', import.meta.url))
          }
        }
      }
  }
})

/**
 * content 开发模式、options 开发模式、options 构建模式
 * @param mode 'content-dev' | 'options'
 */
function createVueConfig(mode: 'content-dev' | 'color-dev' | 'options'): UserConfig {
  const modeValue = mode.split('-')[0]

  const config: UserConfig = {
    base: './',
    root: resolve(__dirname, `src/${modeValue}`),
    publicDir: resolve(__dirname, `src/${modeValue}/public`),
    server: {
      host: '0.0.0.0',
      port: 5173
    },
    build: {
      outDir: resolve(__dirname, `dist/${modeValue}`),
      rollupOptions: {
        input: {
          [modeValue]: resolve(__dirname, `src/${modeValue}/index.html`)
        }
      }
    },
    plugins: [
      {
        name: 'vite-plugin-write-html-server',
        enforce: 'post',
        configureServer({ httpServer, config }) {
          httpServer?.once('listening', () => {
            const address = httpServer.address()
            if (typeof address === 'object' && address?.port) {
              const { outDir } = config.build
              mkdir(outDir, { recursive: true }).finally(() => {
                const output = join(outDir, 'index.html')
                const text = `
                <!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="UTF-8">
                    <script type="module" src="http://localhost:${address.port}/@vite/client"></script>
                    <script type="module" src="http://localhost:${address.port}/index.ts"></script>
                </head>
                <body>
                    <div id="app"></div>
                </body>
                </html>`.replace(/\s+</g, '<')
                writeFile(output, text, { flag: 'w+' })
              })
            }
          })
        }
      }
    ]
  }

  return mergeConfig(baseConfig, config)
}

type ScriptMode = 'background' | 'content' | 'inject'

/**
 * background 开发模式、background 构建模式、content 构建模式
 * @param mode 'background' | 'content'
 */
function createScriptConfig(mode: ScriptMode): UserConfig {
  const formats: Record<ScriptMode, ('es' | 'iife')[]> = {
    background: ['es'],
    content: ['es'],
    inject: ['iife']
  }

  const config: UserConfig = {
    build: {
      minify: false,
      emptyOutDir: false,
      modulePreload: {
        polyfill: false
      },
      lib: {
        name: mode,
        formats: formats[mode],
        entry: {
          [mode]: resolve(__dirname, `./src/${mode}/index.ts`)
        },
        fileName(format, entryName) {
          if (entryName === 'content' || entryName === 'inject') {
            return `${entryName}/index.js`
          }
          return `${entryName}.js`
        }
      },
      rollupOptions: {
        output: {
          assetFileNames({ name }) {
            if (name?.endsWith('.css')) {
              return `${mode}/${name}`
            }
            return name!
          }
        }
      }
    }
  }

  return mergeConfig(baseConfig, config)
}
